package org.multithreadprint;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Copyright (c) 2023-2033 All Rights Reserved
 * 方法 3
 * 题目：三个线程分别打印 A，B，C，要求这三个线程一起运行，打印 n 次，按顺序输出形如“ABCABCABC....”的字符串。
 * 方法：通过 ReentrantLock 加锁控制并发，通过 lock.Condition 实现精确通知，线程状态条件不满足时等待，条件满足时唤醒，控制执行顺序。
 * 实现：
 *      1. Condition 对象由 lock 对象创建，在调用前必须先获取锁
 *      2. Condition 主要通过 await() signal() 方法实现精确通知
 * 结果：
 * A
 * A
 * A
 * ThreadB await
 * C
 * C
 * B
 *
 * @date: 2023/7/14
 */
public class MultiThreadLockCondition2 {
    private static int status;

    private static Lock lock = new ReentrantLock();
    private static Condition condition0 = lock.newCondition();
    private static Condition condition1 = lock.newCondition();
    private static Condition condition2 = lock.newCondition();

    public static void main(String[] args) {
        new Thread(() -> {
            lock.lock();
            try {
                while (status % 3 != 0) {
                    System.out.println(Thread.currentThread().getName() + " await");
                    condition0.await();
                }
                for (int i = 0; i < 3; i++) {
                    System.out.println("A");
                }
                status = 2;
                condition2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "ThreadA").start();

        new Thread(() -> {
            lock.lock();
            try {
                while (status % 3 != 1) {
                    System.out.println(Thread.currentThread().getName() + " await");
                    condition1.await();
                }
                for (int i = 0; i < 1; i++) {
                    System.out.println("B");
                }
                status = 0;
                condition0.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "ThreadB").start();

        new Thread(() -> {
            lock.lock();
            try {
                while (status % 3 != 2) {
                    System.out.println(Thread.currentThread().getName() + " await");
                    condition2.await();
                }
                for (int i = 0; i < 2; i++) {
                    System.out.println("C");
                }
                status = 1;
                condition1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "ThreadC").start();
    }
}
